// This file is part of Module Proxy.

// Module Proxy is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Module Proxy is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Module Proxy.  If not, see <https://www.gnu.org/licenses/>.


//         Copyright (C) 2021 - 2030  关中麦客  
//         All rights reserved
//
//         mod_urlmap.rs
//         唯一URL映射Map
//
//         created by 关中麦客 1036038462@qq.com

use std::collections::HashMap;
use tokio::sync::mpsc::{Sender, channel};
use tokio::sync::Mutex; 
use tokio::sync::oneshot;
use once_cell::sync::OnceCell;

//全局 channel tx
static TX: OnceCell<Mutex<Sender<Method>>> = OnceCell::new();   

/// 映射类型
#[derive(Copy, Clone, PartialEq)]
pub enum MapType
{
    File,   //文件映射类型
    Url,    //URL映射类型
}

/// channel 接口
// #[derive(Debug)]
enum Method
{
    Set 
    {
        key:        String,     //设置key
        mtype:      MapType,    //映射类型
        val:        String,     //映射值
        timeout:    i32,        //超时秒时间戳（-1表示永不超时）
    },
    Get
    {
        key: String,
        rsp: oneshot::Sender<Option<(MapType, String, i32)>>,  
    },
    Del
    {
        key:    String,     //设置key
    },
}

// 初始化
pub fn init()
{    
    //唯一URL非使能（被注释）
    if !super::conf::enable_module_url()
    {
        //log::info!("mod_urlmap disabled!");
        return;  
    }

    log::info!("map of mod_url init...");

    //初始化channel tx rx
    let (tx, mut rx) = channel(32);

    //初始化缓存
    TX.get_or_init(||{
        Mutex::new(tx)
    });

    //缓存
    let mut _cache: HashMap<String, (MapType, String, i32)> = HashMap::new(); 

    //接收任务 ---------------
    tokio::spawn(async move {

        while let Some(method) = rx.recv().await
        {
            match method
            {
                Method::Set{key, mtype, val, timeout} =>
                {
                    let tup: (MapType, String, i32) = (mtype, val, timeout);
                    _cache.insert(key, tup);
                },
                Method::Get{key, rsp} =>
                {
                    if let Some((mtype, val, timeout)) = _cache.get(&key)
                    {
                        //超时
                        if *timeout != -1 && *timeout < super::util::sec_timestamp() as i32
                        {
                            _cache.remove(&key);        //删除映射
                            let _ = rsp.send(None);     //返回None
                            return;
                        }

                        //映射类型
                        let mtype: MapType = *mtype;
                        let val: String = val.clone();  //映射值
                        let tup: (MapType, String, i32) = (mtype, val, *timeout);
                        let _ = rsp.send(Some(tup));
                    }
                    else
                    {
                        let _ = rsp.send(None);
                    } 
                },
                Method::Del{key} =>
                {
                    _cache.remove(&key);
                },
            }
        }
    });
}


/// ----------------------------- 接口 ------------------------------

///发送channel消息 set
pub async fn set(key: String, mtype: MapType, val: String, timeout: i32)
{
    if let Some(mutex) = TX.get()
    {
        let tx = mutex.lock().await;
        let tx = tx.clone();
        let method = Method::Set{key, mtype, val, timeout};
        let _ = tx.send(method).await;        //发送
    }    
}

/// 发送channel消息 get
pub async fn get(key: &str) -> Option<(MapType, String, i32)>
{
    let (rsp_tx, rsp_rx) = oneshot::channel();
    if let Some(mutex) = TX.get()
    {
        let tx = mutex.lock().await;
        let tx = tx.clone();
        let method = Method::Get{key: key.to_string(), rsp: rsp_tx};
        let _ = tx.send(method).await;  //发送
        if let Ok(tup) = rsp_rx.await   //接收
        {
            return tup;
        }
    }

    None
}

///发送channel消息 del
pub async fn del(key: String)
{
    if let Some(mutex) = TX.get()
    {
        let tx = mutex.lock().await;
        let tx = tx.clone();
        let method = Method::Del{key};
        let _ = tx.send(method).await;        //发送
    }    
}
